using System;
using System.Collections.Generic;
using System.Text;

namespace ASMdll
{
	internal static class OneOperandTranslator
	{
		private static int regSet = (1 << 16) - 1;

		private static int regSetByte = (1 | 4 | 16 | 64 | 256 | 1024 | 4096 | 16384);

		private static int sregSet = ((1 << 6) - 1) << 16;

		public static Byte[] ParseLOOP(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Pointer)
				throw new Exception("Error in operand of LOOP command!");

			code = new Byte[2];
			code[0] = (14 << 4) | 2;
			needWriteD8 = true;
			byteToWriteD8 = 1;

			return code;
		}

		public static Byte[] ParseLOOPE(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Pointer)
				throw new Exception("Error in operand of LOOPE command!");

			code = new Byte[2];
			code[0] = (14 << 4) | 1;
			needWriteD8 = true;
			byteToWriteD8 = 1;

			return code;
		}

		public static Byte[] ParseJE(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Pointer)
				throw new Exception("Error in operand of JE command!");

			code = new Byte[2];
			code[0] = (7 << 4) | 4;
			needWriteD8 = true;
			byteToWriteD8 = 1;

			return code;
		}

		public static Byte[] ParseJG(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Pointer)
				throw new Exception("Error in operand of JG command!");

			code = new Byte[2];
			code[0] = (7 << 4) | 15;
			needWriteD8 = true;
			byteToWriteD8 = 1;

			return code;
		}

		public static Byte[] ParseJL(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Pointer)
				throw new Exception("Error in operand of JL command!");

			code = new Byte[2];
			code[0] = (7 << 4) | 12;
			needWriteD8 = true;
			byteToWriteD8 = 1;

			return code;
		}

		public static Byte[] ParseJNE(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Pointer)
				throw new Exception("Error in operand of JNE command!");

			code = new Byte[2];
			code[0] = (7 << 4) | 5;
			needWriteD8 = true;
			byteToWriteD8 = 1;

			return code;
		}

		public static Byte[] ParseJMP(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Pointer)
				throw new Exception("Error in operand of JNE command!");

			code = new Byte[2];
			code[0] = (14 << 4) | 11;
			needWriteD8 = true;
			byteToWriteD8 = 1;

			return code;
		}

		public static Byte[] ParseCALL(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD8 = false;
			byteToWriteD8 = -1;

			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Pointer)
				throw new Exception("Error in operand of JNE command!");

			code = new Byte[3];
			code[0] = (14 << 4) | 8;
			needWriteD16 = true;
			byteToWriteD16 = 1;

			return code;
		}

		public static Byte[] ParseINT(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Immediate)
				throw new Exception("Error in operand!");

			if (op1.Size_ == Operand.Size.Word)
				throw new Exception("Error in operand!");

			if (op1.IM > 255)
				throw new Exception("Error in operand!");

			code = new Byte[2];
			code[0] = (Byte)((12 << 4) | 13);
			code[1] = (Byte)op1.IM;

			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			return code;
		}

		public static Byte[] ParsePUSH(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT == Operand.AddressingType.Immediate) // PUSH IM
			{
				code = new Byte[3];

				code[0] = (Byte)((6 << 4) | 8);
				code[1] = (Byte)(op1.IM & 255);
				code[2] = (Byte)(op1.IM >> 8);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			if (op1.AT == Operand.AddressingType.Register && ((int)op1.R & regSet) != 0) // PUSH R
			{
				code = new Byte[1];
				code[0] = (Byte)((5 << 4));
				code[0] |= Operand.getRegBits(op1.R);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			if (op1.AT == Operand.AddressingType.Register && (op1.R == Operand.Register.ES || op1.R == Operand.Register.CS || op1.R == Operand.Register.SS || op1.R == Operand.Register.DS)) // PUSH ES, CS, SS, DS
			{
				code = new Byte[1];
				code[0] = (Byte)((Operand.getRegBits(op1.R) << 3) | 6);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			if (op1.AT == Operand.AddressingType.Register && (op1.R == Operand.Register.FS || op1.R == Operand.Register.GS)) // PUSH FS, GS
			{
				code = new Byte[2];
				code[0] = 15;
				code[1] = (Byte)((8 << 4) | (Operand.getRegBits(op1.R) << 3));

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			if (op1.AT == Operand.AddressingType.Register)
				throw new Exception("Error in operand!");

			// PUSH M

			if (op1.AT == Operand.AddressingType.RegisterImplicit)
			{
				code = new Byte[2];

				code[0] = (Byte)((15 << 4) | 15);
				code[1] = Operand.GetModRMByte(op1);
				code[1] |= (Byte)(6 << 3);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			code = new Byte[4];

			code[0] = (Byte)((15 << 4) | 15);
			code[1] = Operand.GetModRMByte(op1);
			code[1] |= (Byte)(6 << 3);

			needWriteMemory = true;
			byteIndToWriteMemory = 2;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			return code;
		}

		public static Byte[] ParsePOP(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT == Operand.AddressingType.Immediate)
				throw new Exception("Error in operand!");

			if (op1.AT == Operand.AddressingType.Register && ((int)op1.R & regSet) != 0) // POP R
			{
				code = new Byte[1];
				code[0] = (Byte)((5 << 4) | 8 | Operand.getRegBits(op1.R));

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			if (op1.AT == Operand.AddressingType.Register && (op1.R == Operand.Register.ES || op1.R == Operand.Register.CS || op1.R == Operand.Register.SS || op1.R == Operand.Register.DS)) // POP ES, CS, SS, DS
			{
				code = new Byte[1];
				code[0] = (Byte)((Operand.getRegBits(op1.R) << 3) | 7);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}


			if (op1.AT == Operand.AddressingType.Register && (op1.R == Operand.Register.FS || op1.R == Operand.Register.GS)) // POP FS, GS
			{
				code = new Byte[2];
				code[0] = 15;
				code[1] = (Byte)((8 << 4) | (Operand.getRegBits(op1.R) << 3) | 1);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			if (op1.AT == Operand.AddressingType.Register)
				throw new Exception("Error in operand!");

			// POP M

			if (op1.AT == Operand.AddressingType.RegisterImplicit)
			{
				code = new Byte[2];

				code[0] = (Byte)((8 << 4) | 15);
				code[1] = Operand.GetModRMByte(op1);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			code = new Byte[4];

			code[0] = (Byte)((8 << 4) | 15);
			code[1] = Operand.GetModRMByte(op1);

			needWriteMemory = true;
			byteIndToWriteMemory = 2;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			return code;
		}

		public static Byte[] ParseRET(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT != Operand.AddressingType.Immediate)
				throw new Exception("Error in operand!");

			needWriteMemory = false;
			byteIndToWriteMemory = -1;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;
			code = new Byte[3];
			code[0] = (Byte)((12 << 4) | 2);
			code[1] = (Byte)(op1.IM & 255);
			code[2] = (Byte)(op1.IM >> 8);

			return code;
		}

		public static Byte[] ParseNOT(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT == Operand.AddressingType.Immediate)
				throw new Exception("Error in operand!");

			if (op1.AT == Operand.AddressingType.Register)
			{
				code = new Byte[2];
				code[0] = (Byte)((15 << 4) | 6);
				if (Operand.getSize(op1.R) == Operand.Size.Word)
					code[0] |= 1;
				code[1] = (Byte)(Operand.GetModRMByte(op1) | 16);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			if (op1.AT == Operand.AddressingType.RegisterImplicit)
			{
				code = new Byte[2];
				code[0] = (Byte)((15 << 4) | 7);
				code[1] = (Byte)(Operand.GetModRMByte(op1) | 16);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}


			code = new Byte[4];
			code[0] = (Byte)((15 << 4) | 7);
			code[1] = (Byte)(Operand.GetModRMByte(op1) | 16);

			needWriteMemory = true;
			byteIndToWriteMemory = 2;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			return code;
		}

		public static Byte[] ParseINC(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			Byte T = 0;
			Byte t = (Byte)((4 << 4));

			if (op1.AT == Operand.AddressingType.Immediate)
				throw new Exception("Error in operand!");

			#region INC R

			if (op1.AT == Operand.AddressingType.Register)
			{
				if (((int)op1.R & regSet) == 0)
					throw new Exception("Error in operand!");

				code = new Byte[1];

				code[0] = (Byte)(t | Operand.getRegBits(op1.R));

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region INC M

			if (op1.AT == Operand.AddressingType.RegisterImplicit)
			{
				code = new Byte[2];

				code[0] = (Byte)((15 << 4) | 15);
				code[1] = Operand.GetModRMByte(op1);
				code[1] |= T;

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}


			code = new Byte[4];

			code[0] = (Byte)((15 << 4) | 15);
			code[1] = Operand.GetModRMByte(op1);
			code[1] |= T;

			needWriteMemory = true;
			byteIndToWriteMemory = 2;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			return code;

			#endregion
		}
		
		public static Byte[] ParseDEC(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			Byte T = 8;
			Byte t = (Byte)((4 << 4) | 8);

			if (op1.AT == Operand.AddressingType.Immediate)
				throw new Exception("Error in operand!");

			#region DEC R

			if (op1.AT == Operand.AddressingType.Register)
			{
				if (((int)op1.R & regSet) == 0)
					throw new Exception("Error in operand!");

				code = new Byte[1];

				code[0] = (Byte)(t | Operand.getRegBits(op1.R));

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region DEC M

			if (op1.AT == Operand.AddressingType.RegisterImplicit)
			{
				code = new Byte[2];

				code[0] = (Byte)((15 << 4) | 15);
				code[1] = Operand.GetModRMByte(op1);
				code[1] |= T;

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}


			code = new Byte[4];

			code[0] = (Byte)((15 << 4) | 15);
			code[1] = Operand.GetModRMByte(op1);
			code[1] |= T;

			needWriteMemory = true;
			byteIndToWriteMemory = 2;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			return code;

			#endregion
		}

		public static Byte[] ParseIDIV(Operand op1, ref bool needWriteMemory, ref int byteIndToWriteMemory, ref bool needWriteD8, ref int byteToWriteD8, ref bool needWriteD16, ref int byteToWriteD16)
		{
			Byte[] code = null;

			if (op1.AT == Operand.AddressingType.Immediate)
				throw new Exception("Error in operands!");

			#region IDIV R

			if (op1.AT == Operand.AddressingType.Register)
			{
				if (((int)op1.R & 255) == 0)
					throw new Exception("Error in operands!");

				code = new Byte[2];

				code[0] = (Byte)((15 << 4) | 6);

				if (Operand.getSize(op1.R) == Operand.Size.Word)
					code[0] |= 1;

				code[1] = Operand.GetModRMByte(op1);
				code[1] |= (Byte)(7 << 3);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region IDIV M

			if (op1.AT == Operand.AddressingType.RegisterImplicit)
			{
				code = new Byte[2];

				code[0] = (Byte)((15 << 4) | 7);

				code[1] = Operand.GetModRMByte(op1);
				code[1] |= (Byte)(7 << 3);

				needWriteMemory = false;
				byteIndToWriteMemory = -1;
				needWriteD8 = false;
				byteToWriteD8 = -1;
				needWriteD16 = false;
				byteToWriteD16 = -1;

				return code;
			}

			#endregion

			#region IDIV M

			code = new Byte[4];

			code[0] = (Byte)((15 << 4) | 7);

			code[1] = Operand.GetModRMByte(op1);
			code[1] |= (Byte)(7 << 3);

			needWriteMemory = true;
			byteIndToWriteMemory = 2;
			needWriteD8 = false;
			byteToWriteD8 = -1;
			needWriteD16 = false;
			byteToWriteD16 = -1;

			return code;

			#endregion
		}
	}
}